package org.dromara.web.modbus.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.system.domain.VariableAssociation;
import org.dromara.system.service.ISensorBackUpService;
import org.dromara.system.service.ISensorService;
import org.dromara.system.service.IVariableAssociationService;
import org.dromara.web.modbus.utils.Modbus4jUtils;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
@RequiredArgsConstructor
public class ModbusService {

    private final IVariableAssociationService variableAssociationService;
    private final ISensorService sensorService;
    private final ISensorBackUpService sensorBackUpService;

    /**
     * 根据进程ID计算当前进程应该读取的数据数量
     * @param processId 进程ID
     * @return 当前进程要读取的数据量
     */
    public int getVariableAssociationBatchSize(int processId) {
        // 查询每个进程的数据量
        List<VariableAssociation> variableAssociations = variableAssociationService.selectListByIp(processId);
        return variableAssociations.size(); // 返回该进程应处理的数据数量
    }

    /**
     * 批量并发读取 Modbus 数据
     *
     * @param batchSize   每批次处理的数量
     * @param threadCount   进程ID（决定读取的数据）
     */
    public void batchReadConcurrently(int batchSize, int threadCount) {
        List<VariableAssociation> variableAssociations = variableAssociationService.getVariableAssociationByIdAndSource(7);

        ForkJoinPool customThreadPool = new ForkJoinPool(threadCount);
        try {
            List<CompletableFuture<Void>> futures = new ArrayList<>();

            for (int i = 0; i < variableAssociations.size(); i += batchSize) {
                int start = i;
                int end = Math.min(i + batchSize, variableAssociations.size());
                List<VariableAssociation> batch = variableAssociations.subList(start, end);

                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                    int maxRetries = 3;
                    for (int retry = 0; retry < maxRetries; retry++) {
                        try {
                            readVariableAssociations(batch);
                            break; // 成功则跳出重试循环
                        } catch (Exception e) {
                            log.error("批次读取失败，重试次数: " + retry, e);
                            if (retry == maxRetries - 1) {
                                throw new RuntimeException("批次读取最终失败", e);
                            }
                        }
                    }
                }, customThreadPool);

                futures.add(future);
            }

            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

            if (!customThreadPool.awaitTermination(2, TimeUnit.MINUTES)) {
                throw new RuntimeException("任务超时");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("批量读取任务被中断", e);
        } finally {
            customThreadPool.shutdownNow();
        }
    }

    /**
     * 带重试的批量读取逻辑
     *
     * @param batch      当前批次的数据
     * @param maxRetries 最大重试次数
     */
    private void retryReadBatch(List<VariableAssociation> batch, int maxRetries) {
        for (int retry = 0; retry < maxRetries; retry++) {
            try {
                readVariableAssociations(batch);
                return; // 成功后直接返回
            } catch (Exception e) {
                log.error("批次读取失败，重试次数 [{}]: {}", retry + 1, e.getMessage());
                if (retry == maxRetries - 1) {
                    throw new RuntimeException("批次读取最终失败", e);
                }
            }
        }
    }

    /**
     * 读取单批次的 VariableAssociation 数据
     *
     * @param variableAssociations 当前批次的数据
     * @throws Exception 读取过程中可能抛出的异常
     */
    private void readVariableAssociations(List<VariableAssociation> variableAssociations) throws Exception {
        for (VariableAssociation variableAssociation : variableAssociations) {
            try {
                // Modbus 数据读取
                Number value = Modbus4jUtils.batchReadOne(variableAssociation);

                // 更新实时数据
                variableAssociationService.updateSensorValue(variableAssociation.getId(), value);

                // 插入新数据，如果值变化
                Float lastValue = sensorService.getLatestValue(variableAssociation.getId());
                if (lastValue == null || !lastValue.equals(value.floatValue())) {
                    sensorService.insertData(value.floatValue(), variableAssociation.getId());
                    sensorBackUpService.insertData(value.floatValue(), variableAssociation.getId());
                }

            } catch (Exception e) {
                log.error("读取变量 [{}] 失败: {}", variableAssociation.getId(), e.getMessage(), e);
                throw e; // 抛出异常让外部逻辑决定是否重试
            }
        }
    }
}
